;-------------------------------------------------------------------------------
format ELF64 executable
entry start

SDL_OPENGL = 0x04000000
SDL_NOFRAME = 0x02000000
SDL_KEYDOWN = 0x300

GL_FRAGMENT_SHADER = 0x8B30

DEBUG = 0

macro emit [param] {param}
;-------------------------------------------------------------------------------
include 'import64.inc'
interpreter '/lib64/ld-linux-x86-64.so.2'
needed 'libdl.so.2'
import dlopen,dlsym
;-------------------------------------------------------------------------------
segment readable writeable executable

; rdi: userdata, rsi: dst ptr, edx: length
audio_callback:
    sub rsp,24
    emit <t equ rsp>,<wt equ rsp+4>,<y equ rsp+8>,<e equ rsp+12>
    mov r15,rsi
    mov r13d,edx
      .for_each_sample:
    ; compute t
    emit <cvtsi2ss xmm0,[g_sample]>,<divss xmm0,[g_f32_44100]>,<movss [t],xmm0>
    ; compute wt
    emit <mulss xmm0,[g_f32_2pi]>,<mulss xmm0,[g_f32_f]>,<movss [wt],xmm0>
    ; y=0.6*sin(wt)
    emit <call [sinf]>,<mov eax,0.6>,<movd xmm1,eax>,<mulss xmm0,xmm1>,<movss [y],xmm0>
    ; y*=exp(-0.0008*wt)
    emit <mov eax,-0.0008>,<movd xmm0,eax>,<mulss xmm0,[wt]>,<call [expf]>,<mulss xmm0,[y]>,<movss [y],xmm0>
    ; e=0.3*sin(2*wt)
    emit <movss xmm0,[wt]>,<addss xmm0,xmm0>,<call [sinf]>,<mov eax,0.3>,<movd xmm1,eax>,<mulss xmm0,xmm1>,<movss [e],xmm0>
    ; y+=e*exp(-0.0010*wt)
    emit <mov eax,-0.0010>,<movd xmm0,eax>,<mulss xmm0,[wt]>,<call [expf]>
    emit <mulss xmm0,[e]>,<addss xmm0,[y]>,<movss [y],xmm0>
    ; e=0.1*sin(4*wt)
    emit <mov eax,4.0>,<movd xmm0,eax>,<mulss xmm0,[wt]>,<call [sinf]>,<mov eax,0.1>,<movd xmm1,eax>,<mulss xmm0,xmm1>,<movss [e],xmm0>
    ; y+=e*exp(-0.0015*wt)
    emit <mov eax,-0.0015>,<movd xmm0,eax>,<mulss xmm0,[wt]>,<call [expf]>
    emit <mulss xmm0,[e]>,<addss xmm0,[y]>,<movss [y],xmm0>
    ; y+=0.2*y*y*y
    emit <mov eax,0.2>,<movd xmm0,eax>,<mulss xmm0,[y]>,<mulss xmm0,[y]>,<mulss xmm0,[y]>,<addss xmm0,[y]>,<movss [y],xmm0>
    ; y*=0.9
    emit <mov eax,0.9>,<movd xmm0,eax>,<mulss xmm0,[y]>,<movss [y],xmm0>
    ; y+=0.1*cos(70*t)
    emit <mov eax,70.0>,<movd xmm0,eax>,<mulss xmm0,[t]>,<call [cosf]>
    emit <mov eax,0.1>,<movd xmm1,eax>,<mulss xmm0,xmm1>,<addss xmm0,[y]>,<movss [y],xmm0>
    ; y+=2*y*exp(-22*t)
    emit <mov eax,-22.0>,<movd xmm0,eax>,<mulss xmm0,[t]>,<call [expf]>
    emit <mulss xmm0,[y]>,<addss xmm0,xmm0>,<addss xmm0,[y]>,<movss [y],xmm0>
    ; move to the next sample
    emit <movss xmm0,[y]>,<minss xmm0,[g_f32_1]>,<maxss xmm0,[g_f32_m1]>,<mulss xmm0,[g_f32_32767]>,<cvtss2si eax,xmm0>
    emit <mov [r15],ax>,<add r15,2>
    inc [g_sample]
    cmp [g_sample],1*44100
    jng @f
    mov [g_sample],0
    ; double the frequency
    emit <movss xmm0,[g_f32_f]>,<addss xmm0,xmm0>,<movss [g_f32_f],xmm0>
      @@:
    emit <sub r13d,2>,<jnz .for_each_sample>
    add rsp,24
    ret

start:
    sub rsp,2048
    ; define local variables
    emit <log equ rsp>,<event equ rsp+1024>
    mov r13,[dlsym]
    ; load math functions
    emit <mov rdi,g_str_libm>,<xor esi,esi>,<inc esi>,<call [dlopen]>,<mov r14,rax>
    emit <mov rdi,r14>,<mov rsi,g_str_sinf>,<call r13>,<mov [sinf],rax>
    emit <mov rdi,r14>,<mov rsi,g_str_cosf>,<call r13>,<mov [cosf],rax>
    emit <mov rdi,r14>,<mov rsi,g_str_expf>,<call r13>,<mov [expf],rax>
if DEBUG = 1
    emit <xor edi,edi>,<mov rsi,g_str_printf>,<call r13>,<mov [printf],rax>
end if
    ; load SDL library
    emit <mov rdi,g_str_libsdl>,<xor esi,esi>,<inc esi>,<call [dlopen]>,<mov r12,rax>
    ; SDL_SetVideoMode
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_SetVideoMode>,<call r13>
    emit <mov edi,1280>,<mov esi,720>,<xor edx,edx>,<mov ecx,SDL_OPENGL+SDL_NOFRAME>,<call rax>
    ; load needed SDL functions
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_GL_GetProcAddress>,<call r13>,<mov rbx,rax>
    ; create program
    emit <mov rdi,g_str_glCreateShaderProgramv>,<call rbx>
    emit <mov edi,GL_FRAGMENT_SHADER>,<mov esi,1>,<mov rdx,g_src_fs_ptr>,<call rax>,<mov r15d,eax>
    ; use program
    emit <mov rdi,g_str_glUseProgram>,<call rbx>
    emit <mov edi,r15d>,<call rax>
if DEBUG = 1
    ; print GLSL info log
    emit <mov rdi,g_str_glGetProgramInfoLog>,<call rbx>
    emit <mov edi,r15d>,<mov esi,1024>,<xor edx,edx>,<lea rcx,[log]>,<call rax>
    emit <mov rdi,g_str_log>,<lea rsi,[log]>,<call [printf]>
end if
    ; open audio device
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_OpenAudio>,<call r13>
    emit <mov rdi,g_audio_spec>,<xor esi,esi>,<call rax>
    ; start audio device
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_PauseAudio>,<call r13>
    emit <xor edi,edi>,<call rax>

      .main_loop:
    ; process window events
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_PollEvent>,<call r13>
    emit <lea rdi,[event]>,<call rax>
    cmp dword [event],SDL_KEYDOWN
    je .main_loop_end
    ; get time
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_GetTicks>,<call r13>,<call rax>,<mov r14d,eax>
    emit <mov rdi,g_str_glTexCoord1f>,<call rbx>
    emit <cvtsi2ss xmm0,r14d>,<call rax>
    ; draw
    emit <mov rdi,g_str_glRecti>,<call rbx>
    emit <mov edi,-1>,<mov esi,-1>,<mov edx,1>,<mov ecx,1>,<call rax>
    ; swap
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_GL_SwapBuffers>,<call r13>,<call rax>
    jmp .main_loop
      .main_loop_end:

    ; close audio device
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_CloseAudio>,<call r13>,<call rax>
    ; terminate process
    emit <mov eax,60>,syscall
;-------------------------------------------------------------------------------
align 1
g_src_fs:
db  'float t;'
db  'void main(){'
db      't=gl_TexCoord[0].s*.001;'
db      'gl_FragColor=vec4(.5+.5*sin(t));'
db  '}',0

g_str_libsdl db 'libSDL-1.3.so.0',0
g_str_SDL_SetVideoMode db 'SDL_SetVideoMode',0
g_str_SDL_PollEvent db 'SDL_PollEvent',0
g_str_SDL_GL_SwapBuffers db 'SDL_GL_SwapBuffers',0
g_str_SDL_GetTicks db 'SDL_GetTicks',0
g_str_SDL_GL_GetProcAddress db 'SDL_GL_GetProcAddress',0
g_str_glCreateShaderProgramv db 'glCreateShaderProgramv',0
g_str_glRecti db 'glRecti',0
g_str_glUseProgram db 'glUseProgram',0
g_str_glTexCoord1f db 'glTexCoord1f',0
g_str_SDL_OpenAudio db 'SDL_OpenAudio',0
g_str_SDL_PauseAudio db 'SDL_PauseAudio',0
g_str_SDL_CloseAudio db 'SDL_CloseAudio',0
g_str_libm db 'libm.so.6',0
g_str_sinf db 'sinf',0
g_str_cosf db 'cosf',0
g_str_expf db 'expf',0
if DEBUG = 1
g_str_printf db 'printf',0
g_str_glGetProgramInfoLog db 'glGetProgramInfoLog',0
g_str_log db 'GLSL info log:',10,'%s',10,0
g_str_d db '%d',10,0
g_str_p db '%p',10,0
g_str_f db '%f',10,0
end if

align 4
g_sample dd 0
g_f32_44100 dd 44100.0
g_f32_32767 dd 32767.0
g_f32_2pi dd 6.28318531
g_f32_f dd 100.0
g_f32_m1 dd -1.0
g_f32_1 dd 1.0

align 8
g_src_fs_ptr dq g_src_fs
sinf dq 0
cosf dq 0
expf dq 0
if DEBUG = 1
printf dq 0
end if

g_audio_spec:
dd 44100
dw 0x8010
db 1,?
dw 2048,?
dd ?
dq audio_callback
dq 0
;-------------------------------------------------------------------------------
